﻿/**
 * 谱序编辑器主类，包含编辑器提供的大部分公用接口
 * @file
 * @module puxu
 * @class EdPuXu
 * @since 1.1.0.0
 */
'use strict';
var puxu = window.puxu || {};

puxu.version = "1.1.0";
puxu.Editarea = "w_left";

(function () {
	var uid = 0

	puxu.EdPuXu = function (options) {
		var me = this;
		me.uid = uid++;
		puxu.uid = me.uid;
		puxu.options = options;
		me.ID = options.ID;
		me.LeftID = options.LeftID;
		me.RightID = options.RightID;
		me.PuXuTexT = options.PuXuTexT;
		me.commands = {};
		me.options = utils.extend(utils.clone({}), PuXu_CONFIG, true);
		//puxu.options.push(me.options);
		me.shortcutkeys = {};
		me.inputRules = [];
		me.outputRules = [];
		//设置默认的常用属性
		let Options = puxu.EdPuXu.defaultOptions(me)
		me.setOpt(Options);
		Object.assign(puxu.options, Options);

		//------------------------参数配置 开始------------------------
		// 不需要的工具栏菜单
		this.notMenu = [];
		//编辑器的尺寸
		this.width = '100%';
		this.height = '400px';
		//查看源码
		this.code = 0;
		//窗口最大化和最小化
		this.maxWindow = 1;
		//编辑器禁用
		this.disabled = 0;
		//编辑器样式
		this.css = '';
		//图片和附件提交地址
		this.uploadUrl = 0;
		//纯文本粘贴
		this.pasteText = 1;
		//截图粘贴启用
		this.screenshot = 1;
		//截图粘贴直接上传到服务器
		this.screenshotUpload = 1;
		//网络图片上传到服务器
		this.imgAutoUpload = 1;
		//图片下载到本地的域名，默认为本地域名（false），其它域名为数组类型
		this.imgDomain = 0;
		//上传监听
		this.ajax.uploadTimeout = 15000; 	//ajax超时时间
		this.ajax.xhr = function () { }; 		//ajax的xhr设置
		this.ajax.timeout = function () { }; 	//ajax超时回调
		this.ajax.progress = function () { }; 	//ajax进度回调
		this.ajax.success = function () { }; 	//ajax成功回调
		this.ajax.error = function () { }; 	//ajax失败回调
		this.ajax.complete = function () { }; 	//ajax成功或失败都回调
		//上传附件
		this.filesUpload = {};
		this.filesUpload.success = function () { };
		this.filesUpload.error = function () { };
		this.filesUpload.complete = function () { };
		//上传图片
		this.imgUpload = {};
		this.imgUpload.success = function () { };
		this.imgUpload.error = function () { };
		this.imgUpload.complete = function () { };


		//HTML标签过滤黑名单-忽略粘贴过来的HTML标签
		this.filterTag = ['meta', 'script', 'object', 'form', 'iframe'];
		//style过滤黑名单-忽略粘贴过来的style样式
		this.filterStyle = ['background-image'];
		//块级元素
		this.blockTag = ['address', 'caption', 'dd', 'div', 'dl', 'dt', 'fieldset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'legend', 'fieldset', 'li', 'noframes', 'noscript', 'ol', 'ul', 'p', 'pre', 'table', 'tbody', 'tfoot', 'th', 'thead', 'tr'];
		//------------------------参数配置 结束------------------------
		//构建功能模块唯一id
		this.getTime = '1' + String(new Date().getTime()).substr(4, 8);
		this.toolId = 'PuXuTool' + me.uid;

		//菜单列表对象
		this.menuList = {};

		//获取编辑器对象
		/*		var _z = this;*/
		this.EdPuXu = this.id(options.ID);
		if (!this.EdPuXu) return alert('请提供一个有效的id');
		this.textarea = 0;
		// 只能是 textarea 和 div ，其他类型的元素不行
		if (this.EdPuXu.nodeName !== 'TEXTAREA' && this.EdPuXu.nodeName !== 'DIV') {
			return console.log('EdPuXu：暂不支持该标签「' + this.EdPuXu.nodeName + '」，推荐使用div或textarea');
		}
		if (this.EdPuXu.nodeName == 'TEXTAREA') {
			this.EdPuXu.style.display = 'none';
			this.divId = '_div' + this.getTime;
			var div = this.c('div');
			div.className = 'puxuEditor';
			div.id = this.divId;
			this.insertAfter(div, this.EdPuXu);

			//加载编辑器的内容
			this.textarea = this.EdPuXu;
			this.EdPuXu = this.id(this.divId);
			this.value = this.textarea.value;
		} else {
			this.EdPuXu.className = 'puxuEditor';
			this.value = this.EdPuXu.innerHTML;
			this.value_left = this.id(me.LeftID).innerHTML;
			this.value_right = this.id(me.RightID).innerHTML;
			this.EdPuXu.innerHTML = '';
		}
		// 创建编辑器外框区域
		this.PuX_ucontent = this.c('div');
		this.PuX_ucontent.className = 'PuXu_content';

		//创建左边编辑区
		this.PuXu_E_left = this.c('div');
		this.PuXu_E_left.className = 'PuXu_E_left';
		this.PuX_ucontent.appendChild(this.PuXu_E_left);

		//创建中间显示谱许信息
		this.PuXu_E_center = this.c('div');
		this.PuXu_E_center.className = 'PuXu_E_center';
		this.PuXu_E_center.innerHTML = '<div class="cent-top"><span>' + me.PuXuTexT.top + '</span></div><div class="cent-middle"><span>' + me.PuXuTexT.middle.left + '</span><em>' + me.PuXuTexT.middle.right + '</em></div><div class="cent-foot"><em class="left">' + me.PuXuTexT.foot.left + '</em><span>' + me.PuXuTexT.foot.centre + '</span><em class="right">' + me.PuXuTexT.foot.right + '</em></div>';
		this.PuX_ucontent.appendChild(this.PuXu_E_center);

		//创建右边编辑区
		this.PuXu_E_right = this.c('div');
		this.PuXu_E_right.className = 'PuXu_E_right';
		this.PuX_ucontent.appendChild(this.PuXu_E_right);

		this.EdPuXu.appendChild(this.PuX_ucontent);

		//创建编辑器菜单栏容器
		this.PuXuTool = this.c('div');
		this.PuXuTool.id = this.toolId;
		this.PuXuTool.className = 'PuXu_Tool';

		//创建编辑器菜单栏
		this.Tool_body = this.c('div');
		this.Tool_body.className = 'Tool_body';
		this.PuXuTool.appendChild(this.Tool_body);

		//创建编辑器菜单栏底部按钮区
		this.Tool_foot = this.c('div');
		this.Tool_foot.className = 'Tool_foot';
		this.PuXuTool.appendChild(this.Tool_foot);

		this.EdPuXu.appendChild(this.PuXuTool);

		//创建iframe  iframeleft左边编辑区
		this.iframeleft = this.c('iframe');
		this.iframeleft.id = 'iframeleft_' + me.uid;
		this.iframeleft.className = 'Editiframe';
		this.iframeleft.frameBorder = 0;
		this.PuXu_E_left.appendChild(this.iframeleft);

		//创建iframe  iframeright右边编辑区
		this.iframeright = this.c('iframe');
		this.iframeright.id = 'iframeright_' + me.uid;
		this.iframeright.className = 'Editiframe';
		this.iframeright.frameBorder = 0;
		this.PuXu_E_right.appendChild(this.iframeright);

		//创建禁用编辑器的遮罩
		this.disableds = this.c('div');
		this.disableds.className = 'pux-disabled';
		this.EdPuXu.appendChild(this.disableds);

		//获取iframe对象
		this.w_left = this.iframeleft.contentWindow; //获取iframe Window 对象
		this.d_left = this.iframeleft.contentDocument; //获取iframe documen 对象
		this.w_right = this.iframeright.contentWindow; //获取iframe Window 对象
		this.d_right = this.iframeright.contentDocument; //获取iframe documen 对象

		
		var writehtml = function (value) {
			return '<html class="view">' +
				'<head>' +
				//'<style>' +
				//'.view{padding:0;word-wrap:break-word;cursor:text;}' +
				//'body{font-family:"Microsoft YaHei";font-size:14px;color:#363636;margin:0px;word-wrap:break-word;word-break:break-all;}' +
				//'img{max-width:100%;}' +
				//'p{margin:0;margin-bottom:5px;min-height:19px;}' +
				//'code{color:#696969;background:#eee;padding:2px 5px;display: inline-block;margin:0 5px;border-radius:3px;border:1px solid #dedede;}' +
				//'.pux-code{font-family:Courier New;font-size:13px;line-height:20px;white-space:pre-wrap;}' +
				//'table{margin:10px 0;}' +
				//'table td{padding:8px;line-height:1.42857143;border:1px solid #bdbdbd;}' +
				//'pre{background:#f3f3f3;padding:10px;border-radius:3px;font-family:"Microsoft YaHei";}' +
				//'</style>' +
				(me.options.iframeCssUrl ? "<link rel='stylesheet' type='text/css' href='" + utils.unhtml(me.options.iframeCssUrl) + "'/>" : "") +
				//(me.options.iframejs ? "<script type='text/javascript' src='" + utils.unhtml(me.options.iframejs) + "'></script>" : "") +
				'</head>' +
				'<body>' + value + '</body>' +
				'</html>';
		}

		//为了兼容万恶的IE 创建iframe中的body
		this.d_left.open();
		var valueleft = this.value_left;
		this.d_left.write(writehtml(valueleft));
		this.d_left.close();

		

		// 设置元素为可编辑
		this.d_left.body.designMode = 'on';  //打开设计模式
		this.d_left.body.contentEditable = true;// 设置元素为可编辑
		this.d_left.body.addEventListener('click', function () {
			puxu.Editarea = "w_left";
			me.init(puxu.Editarea);   //初始化参数
			me.parentTagName = me.range.anchorNode.parentNode.tagName;
			for (var i = 0; i < me.options.toolbars.length; i++) {
				if (me.options.toolbars[i] == 'line') continue;
				let Item = me.options.toolbars[i];
				for (var n = 0; n < Item.length; n++) {
					var a = me.menuList[Item[i]];
					if (a != undefined) {
						if (me.d_left.queryCommandState(Item[i])) {
							a.className = 'pux-actives';
						} else {
							if (a.className != 'pux-line' && a.className != 'pux-active-s') a.className = '';
						}
					}
				}
			}
		});
		//$(this.d_left.head).append("<script type='text/javascript' src='" + utils.unhtml(me.options.iframejs) + "'></script>");
		//this.d_left.head.appendChild("<script type='text/javascript' src='" + utils.unhtml(me.options.iframejs) + "'></script>");

		//为了兼容万恶的IE 创建iframe中的body
		this.d_right.open();
		var valueright = this.value_right;
		this.d_right.write(writehtml(valueright));
		this.d_right.close();

		// 设置元素为可编辑
		this.d_right.body.designMode = 'on';  //打开设计模式
		this.d_right.body.contentEditable = true;// 设置元素为可编辑
		this.d_right.body.addEventListener('click', function () {
			puxu.Editarea = "w_right";
			me.init(puxu.Editarea);   //初始化参数
			me.parentTagName = me.range.anchorNode.parentNode.tagName;
			for (var i = 0; i < me.options.toolbars.length; i++) {
				if (me.options.toolbars[i] == 'line') continue;
				let Item = me.options.toolbars[i];
				for (var n = 0; n < Item.length; n++) {
					var a = me.menuList[Item[i]];
					if (a != undefined) {
						if (me.d_right.queryCommandState(Item[i])) {
							a.className = 'pux-actives';
						} else {
							if (a.className != 'pux-line' && a.className != 'pux-active-s') a.className = '';
						}
					}
				}
			}
		});
		//$(this.d_right.head).append("<script type='text/javascript' src='" + utils.unhtml(me.options.iframejs) + "'></script>");

		//内容区
		this.content = new Object();
		this.content['w_left'] = this.d_left.body;
		this.content['w_right'] = this.d_right.body;

		//改变textarea内容
		if (this.textarea) { setInterval(function () { me.setTextarea(); }, 1000); }

		this.init(puxu.Editarea);   //初始化参数
		this.create(); //创建编辑器
		this.paste();  //监听粘贴
	};

	//光标控制器
	puxu.EdPuXu.prototype = {
		id: function (a) { return document.getElementById(a) },
		c: function (a) { return document.createElement(a) },
		//初始化参数
		init: function (str) {
			this.files = null;
			this.insertImage = null;
			if (str == 'w_left') {
				this.element = this.d_left.body;
				//this.element.focus(); //默认获取焦点
				this.range = this.d_left.createRange ? this.w_left.getSelection() : this.d_left.selection.createRange();
				//this.w_left.divBox();
			} else {
				this.element = this.d_right.body;
				//this.element.focus(); //默认获取焦点
				this.range = this.d_right.createRange ? this.w_right.getSelection() : this.d_right.selection.createRange();
				//this.w_right.divBox();
			}
			
			let list = $('.Tool_foot .jus_between .list');
			list.removeClass('active');
			list.each(function () {
				let iframe = $(this).attr('data-iframe');
				if (str == 'w_left') {
					if (iframe == 'Editiframeleft') { $(this).addClass('active'); }
				} else {
					if (iframe == 'Editiframeright') { $(this).addClass('active'); }
				}
			});
		},
		//设置textarea
		setTextarea: function (n, obj) {
			if (!this.textarea) return;
			var html = this.code ? this.html(this.getHTML()) : this.getHTML();
			html = html.replace(/<pre(.*?)>(.*?)<\/pre>/gi, function (all, a = '', b = '') {
				return '<pre' + a + '>' + b.split('<br>').join("\n") + '</pre>';
			});
			this.textarea.value = html;
		},
		//dom后面插入节点
		insertAfter: function (n, obj) {
			var parent = obj.parentNode;
			if (parent.lastChild == obj) {
				parent.appendChild(n, obj);
			} else {
				parent.insertBefore(n, obj.nextSibling);
			}
		},
		//插入HTML
		setHTML: function (html, a) {
			this.element.focus();
			var range = this.range.getRangeAt(0);
			//将选中的文档放在html中的DOM内
			if (!a) html.appendChild(range.extractContents());
			//删除选中的内容
			range.deleteContents();
			//创建文档碎片并放入新节点
			if (puxu.Editarea == "w_left")
				range.insertNode(this.w_left.document.createDocumentFragment().appendChild(html));//合并范围至末尾
			else
				range.insertNode(this.w_right.document.createDocumentFragment().appendChild(html));//合并范围至末尾
			//合并范围至末尾
			range.collapse(false);
		},
		//插入文字内容
		setText: function (text, a) {
			this.element.focus();
			var range = this.range.getRangeAt(0);
			range.deleteContents();
			var el = document.createElement('div');
			if (a) {//是否为html
				el.innerHTML = text;
			} else {
				el.appendChild(document.createTextNode(text));
			}
			var frag = document.createDocumentFragment(), node, lastNode;
			while ((node = el.firstChild)) {
				lastNode = frag.appendChild(node);
			}
			range.insertNode(frag);
			if (lastNode) {
				range = range.cloneRange();
				range.setStartAfter(lastNode);
				range.collapse(true);
				this.range.removeAllRanges();
				this.range.addRange(range);
			}
			range.collapse(true);
		},
		//获取选中的HTML
		getSelectHTML: function () {
			var p = this.c('p');
			p.appendChild(this.range.getRangeAt(0).cloneContents());
			return p.innerHTML;
		},
		//获取选中的内容
		getSelectText: function () {
			if (this.range.toString() == 'false' || this.range.toString() == '') {
				return '';
			} else {
				return this.range.toString();
			}
		},
		unhtml: function (str) {
			var s = '';
			if (str.length == 0) return '';
			s = str.replace(/&/g, "&amp;");
			s = s.replace(/</g, "&lt;");
			s = s.replace(/>/g, "&gt;");
			s = s.replace(/\'/g, "&#39;");
			s = s.replace(/\"/g, '&quot;');
			return s;
		},
		html: function (str) {
			var s = '';
			if (str.length == 0) return '';
			s = str.replace(/&lt;/g, "<");
			s = s.replace(/&gt;/g, ">");
			s = s.replace(/&#39;/g, "\'");
			s = s.replace(/&quot;/g, "\"");
			s = s.replace(/&amp;/g, "&");
			return s;
		},
		//转义：HTML转成字符串
		toText: function (html) {
			var temp = this.c('div');
			(temp.textContent != null) ? (temp.textContent = html) : (temp.innerText = html);
			var output = temp.innerHTML;
			temp = null;
			return output;
		},
		//转义：字符串转成HTML
		toHTML: function (text) {
			var temp = this.c('div');
			temp.innerHTML = text;
			var output = temp.innerText || temp.textContent;
			temp = null;
			return output;
		},
		//判断祖先节点是否存在
		inNodeParent: function (el, parent) {
			if (!el) return false;
			if (el.parentNode) {
				if (typeof parent == 'string') {
					parent = parent.toUpperCase();
					if (el.tagName == parent) return true;
					return el.parentNode.tagName == parent ? true : this.inNodeParent(el.parentNode, parent);
				} else {
					return el.parentNode == parent ? true : this.inNodeParent(el.parentNode, parent);
				}
			}
			return false;
		},
		//数组查询
		inArray: function (needle, array) {
			if (typeof needle == 'string' || typeof needle == 'number') {
				for (var i in array) if (needle === array[i]) return true;
				return false;
			}
		},
		// 获取 range 对象
		getRange: function getRange() {
			return this.range.getRangeAt(0);
		},
		//弹窗
		popup: function (options) {
			options = options || {};
			var width = options.width || '400';     //默认宽度
			var height = options.height || '200';   //默认高度
			var title = options.title || '';        //默认不显示标题
			var content = options.content || '';    //默认内容
			return '<div class="pux-popup"><div class="pux-popupBox"></div><div class="pux-popupMain" style="width:' + width + 'px;height:' + height + 'px;"><div class="pux-popupTitle"><span>' + title + '</span><span class="pux-popupClose">╳</span></div><div class="pux-popupContent">' + content + '</div></div></div>';
		},
		//获取对象距离窗口页面的顶部和左部的距离
		getCoords: function (el) {
			var box = el.getBoundingClientRect(),
				doc = el.ownerDocument,
				body = doc.body,
				html = doc.documentElement,
				clientTop = html.clientTop || body.clientTop || 0,
				clientLeft = html.clientLeft || body.clientLeft || 0,
				top = box.top - clientTop,
				left = box.left - clientLeft;
			return { 'top': top, 'left': left };
		},
		//阻止冒泡
		pd: function (event) {
			window.event ? window.event.cancelBubble = true : e.stopPropagation();
		},
		//是否为ie
		isIE: function () { return !!window.ActiveXObject || "ActiveXObject" in window },
		//异步请求
		ajax: function (json) {
			var me = this;
			json = json || {};
			if (!json.url) return;
			json.timeout = json.timeout || me.ajax.uploadTimeout;
			json.data = json.data || {};
			var json2url = function (json) {
				var arr = [];
				for (var name in json) {
					arr.push(name + '=' + encodeURIComponent(json[name]));
				}
				return arr.join('&');
			}

			//创建
			var xhr = new XMLHttpRequest();
			//xhr.withCredentials = false;
			//连接 和 发送 - 第二步

			//监听进度事件 
			xhr.addEventListener('progress', progress, false);

			xhr.open('POST', json.url, true);
			//设置表单提交时的内容类型
			xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
			if (json.data instanceof FormData == false) {
				xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			}
			me.ajax.xhr(xhr);
			xhr.send(json.data instanceof FormData ? json.data : json2url(json.data));

			//接收 - 第三步
			json.loading && json.loading();
			json.timer = setTimeout(function () {
				xhr.onreadystatechange = null;
				me.ajax.timeout(xhr);
				json.error && json.error('网络超时。');
			}, json.timeout);
			xhr.onreadystatechange = function () {
				if (xhr.readyState === 4 && xhr.status === 200) {
					clearTimeout(json.timer);
					if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
						var res = '';
						if (xhr.responseText.length > 0) res = JSON.parse(xhr.responseText);
						me.ajax.success(res, xhr);
						me.ajax.complete(res, xhr);
						json.success && json.success(res);
					} else {
						me.ajax.error(xhr);
						me.ajax.complete(xhr);
						json.error && json.error(xhr);
					}
				} else {
					me.ajax.error(xhr);
					me.ajax.complete(xhr);
				}

			};
			//上传进度
			function progress(evt) {
				var percent = 0;
				//百分比
				percent = evt.lengthComputable ? Math.round(evt.loaded / evt.total * 100) : 0;
				me.ajax.progress(percent, evt, xhr);
			}
		},
		//创建菜单
		createMenu: function (json) {
			var me = this;
			var li = this.c('div');
			if (json.id) li.id = json.id;
			if (json.style) this.css += json.style;
			if (!json.menu) json.menu = json.title;
			//将菜单设置成文字或者图标
			if (json.menu || json.css) {
				var div = this.c('a');
				if (json.title) div.title = json.title;
				div.className = 'pux_btn';
				if (json.css) div.className += ' ' + json.css;
				if (json.menu) {
					div.innerHTML = '<span>' + json.menu + '</span>';
				} else if (json.icon) {
					if (json.icon) div.innerHTML = '<svg class="pux-icon" aria-hidden="true"><use xlink:href="#icon-' + json.icon + '"></use></svg>';
				}
				if (json.dropdown) { div.innerHTML += '<i class="caret"></i>'; }
				if (json.data) div.setAttribute('data', json.data);
				li.appendChild(div);
			}
			//使用下拉菜单
			if (json.dropdown) {
				var div = this.c('div');
				div.className = 'pux-menuDropdown';
				div.innerHTML = json.dropdown;
				li.appendChild(div);
				li.openMenu = 1;
				li.onmouseover = function () {
					if (li.openMenu) this.className = 'pux-menuActive';
					//if (li.openMenu) div.className = 'pux-menuDropdown pux-menuActive';
				}
				li.onmouseout = function () {
					//div.className = 'pux-menuDropdown';
					this.className = '';
				}
				var exec = div.getElementsByClassName('pux_lia');
				for (var i = 0; i < exec.length; i++) {
					exec[i].addEventListener('click', function () {
						div.className = 'pux-menuDropdown';
						li.openMenu = 0;
						setTimeout(function () { li.openMenu = 1 }, 500);
					})
				}
			}
			//使用弹窗
			if (json.popup) {
				li.innerHTML += this.popup(json.popup);
				li.popup = li.getElementsByClassName('pux-popup')[0];
				li.onclick = function () {
					li.popup.style.display = 'block';
					li.popup.getElementsByClassName('pux-popupClose')[0].onclick = function () {
						li.popup.style.display = 'none';
						me.pd();
					}
				}
				li.close = function () {
					li.popup.style.display = 'none';
					me.pd();
				}
			}
			li.success = json.success ? json.success : false;
			//菜单的点击事件
			if (json.click) li.onclick = function () { json.click(this, me) };
			this.menuList[json.name] = li;
		},
		//插件开发
		plugin: function (json) {
			if (json.name == undefined) return console.log('plugin：menu参数不能为空');
			if (this.inArray(json.name, this.menu)) return console.log('plugin：menu已经存在，请重新命名');
			this.menu.push(json.name);
			this.createMenu(json);
		},
		/**
		 * 该方法是提供给插件里面使用，以{key:value}集合的方式设置插件内用到的配置项默认值
		 * @method setOpt
		 * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
		 * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用，其他地方不能调用。
		 * @param { Object } options 将要设置的选项的键值对对象
		 * @example
		 * ```javascript
		 * editor.setOpt({ 'initContent': '欢迎使用编辑器' });
		 * ```
		 */
		setOpt: function (key, val) {
			var obj = {};
			if (utils.isString(key)) { obj[key] = val; } else { obj = key; }
			utils.extend(this.options, obj, true);
		},
		/**
		 * 渲染编辑器的DOM到指定容器
		 * @method render
		 * @param { Element } containerDom 直接指定容器对象
		 * @remind 执行该方法,会触发ready事件
		 * @warning 必须且只能调用一次
		 */
		render: function (container) {
			var me = this, options = me.options;


		}
	};
})();